package com.syf.filter;

import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Set;

/**
 * Gateway(网关)过滤器:    【页面请求网关80端口，先经过了网关，才会到达这里】
 *      1.在项目中实现一个拦截器，需要继承两个类：GlobalFilter(全局过滤拦截器), Ordered(拦截器的顺序)
 *      2.参数:
 *          (GatewayFilterChain)chain.filter():当前过滤器可以决定是否执行下一个过滤器的逻辑
 *          ServerWebExchange: 存储当前请求和响应的上下文
 */
@Component
public class JwtCheckFilter implements GlobalFilter, Ordered {

    @Autowired
    private StringRedisTemplate redisTemplate ; //redis

    //sys登录、会员图形验证码(获取极验的第一次数据包)、会员登录、会员注册、会员SMS短信验证码发送、会员重置密码(忘记密码)
    @Value("${no.require.urls:/admin/login,/admin/image/AliYunImgUpload,/user/gt/register,/admin/mail/send,/admin/smallclass/before/selSoftClassify}")
    private Set<String> noRequireTokenUris ; //存储所有无需token即可访问的资源路径


    /**
     * 过滤器拦截到用户的请求后做啥【主体】
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //暂时不检验token
        return chain.filter(exchange) ;

       /* // 1: 该接口是否需要token 才能访问
        if(!isRequireToken(exchange)){
            return chain.filter(exchange) ;// 不需要token ,直接放行
        }

        // 2: 取出用户的token
        String token = getUserToken(exchange) ;

        // 3: 判断用户的token 是否有效
        if(StringUtils.isEmpty(token)){
            return buildeNoAuthorizationResult(exchange);  //当token=null时，响应一个没有token的错误
        }
        Boolean hasKey = redisTemplate.hasKey(token); //判断redis中是否存储此token
        if(hasKey!=null && hasKey){
            return chain.filter(exchange) ;// token有效 ,直接放行(redis中有token且处于有效期)
        }
        return buildeNoAuthorizationResult(exchange) ;*/

    }




    /**
     * 1.给用户响应一个没有token的错误 【使用webflux输入请求信息】
     * @param exchange
     * @return
     */
    private Mono<Void> buildeNoAuthorizationResult(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();//响应对象
        // 1.响应头 指定编码，否则在浏览器中会中文乱码  "application/json;charset=UTF-8"   add()
        response.getHeaders().set("Content-Type","application/json");
        response.setStatusCode(HttpStatus.UNAUTHORIZED) ; //2.响应码401 权限不足
        //3.响应信息
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("error","NoAuthorization") ;
        jsonObject.put("errorMsg","Token is Null or Error") ;
        DataBuffer wrap = response.bufferFactory().wrap(jsonObject.toJSONString().getBytes());
        return response.writeWith(Flux.just(wrap)) ;   //Mono.just(wrap)
    }

    /**
     * 2.从请求头里面获取用户的token:
     *      当有token时，去掉"bearer ":("Authorization" : "bearer +token值")
     *          请求头Headers存储token时，在Authorization属性中 存储"bearer K42mn552k2n4rk2n2k2n3k2(获取到的access_token)"
     *
     */
    private String getUserToken(ServerWebExchange exchange) {
        String token = exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION); //获取token
        return token ==null ? null : token.replace("bearer ","") ; //bearer后边有一个空格
    }

    /**
     * 3.判断该 接口(URI)是否需要token
     * @param exchange
     * @return
     */
    private boolean isRequireToken(ServerWebExchange exchange) {
        String path = exchange.getRequest().getURI().getPath(); //获取当前请求的uri
        if(noRequireTokenUris.contains(path)){
            return false ; // 不需要token
        }
        return Boolean.TRUE ;
    }


    /**
     * 4.过滤器执行顺序,数值越小,优先级越高 【拦截第一个】
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}
